package 设计原则和思想.封装;


import java.math.BigDecimal;

/**
 * 封装特性：封装也叫作信息隐藏或者数据访问保护
 *  金融系统中一个简化版的虚拟钱包的代码实现。在金融系统中，我们会给每个用户创建一个虚拟钱包，用来记录用户在我们的系统中的虚拟货币量。
 *  Wallet 类主要有四个属性（也可以叫作成员变量），也就是我们前面定义中提到的信息或者数据。
 *      其中，id 表示钱包的唯一编号，
 *      createTime 表示钱包创建的时间，
 *      balance 表示钱包中的余额，
 *      balanceLastModifiedTime 表示上次钱包余额变更的时间。
 *
 *  参照封装特性，对钱包的这四个属性的访问方式进行了限制。调用者只允许通过下面这六个方法来访问或者修改钱包里的数据。
 *      String getId()
 *      long getCreateTime()
 *      BigDecimal getBalance()
 *      long getBalanceLastModifiedTime()
 *      void increaseBalance(BigDecimal increasedAmount)
 *      void decreaseBalance(BigDecimal decreasedAmount)
 *
 *  之所以这样设计，是因为从业务的角度来说，
 *      id、createTime 在创建钱包的时候就确定好了，之后不应该再被改动，所以，我们并没有在 Wallet 类中，暴露 id、createTime 这两个属性的任何修改方法，比如 set 方法。
 *      而且，这两个属性的初始化设置，对于 Wallet 类的调用者来说，也应该是透明的，所以，在 Wallet 类的构造函数内部将其初始化设置好，而不是通过构造函数的参数来外部赋值。
 *      对于钱包余额 balance 这个属性，从业务的角度来说，只能增或者减，不会被重新设置。所以，在 Wallet 类中，只暴露了 increaseBalance() 和 decreaseBalance() 方法，并没有暴露 set 方法。
 *      对于 balanceLastModifiedTime 这个属性，它完全是跟 balance 这个属性的修改操作绑定在一起的。只有在 balance 修改的时候，这个属性才会被修改。
 *          所以，把 balanceLastModifiedTime 这个属性的修改操作完全封装在了 increaseBalance() 和 decreaseBalance() 两个方法中，
 *          不对外暴露任何修改这个属性的方法和业务细节。这样也可以保证 balance 和 balanceLastModifiedTime 两个数据的一致性。
 */
public class Wallet {
    private String id;
    private long createTime;
    private BigDecimal balance;
    private long balanceLastModifiedTime;
    // ...省略其他属性...

    public Wallet() {
        this.id = IdGenerator.getInstance();
        this.createTime = System.currentTimeMillis();
        this.balance = BigDecimal.ZERO;
        this.balanceLastModifiedTime = System.currentTimeMillis();
    }

    public String getId() { return this.id; }
    public long getCreateTime() { return this.createTime; }
    public BigDecimal getBalance() { return this.balance; }
    public long getBalanceLastModifiedTime() { return this.balanceLastModifiedTime;  }

    public void increaseBalance(BigDecimal increasedAmount) throws Exception {
        if (increasedAmount.compareTo(BigDecimal.ZERO) < 0) {
            throw new Exception("...");
        }
        this.balance.add(increasedAmount);
        this.balanceLastModifiedTime = System.currentTimeMillis();
    }

    public void decreaseBalance(BigDecimal decreasedAmount) throws Exception {
        if (decreasedAmount.compareTo(BigDecimal.ZERO) < 0) {
            throw new Exception("...");
        }
        if (decreasedAmount.compareTo(this.balance) > 0) {
            throw new Exception("...");
        }
        this.balance.subtract(decreasedAmount);
        this.balanceLastModifiedTime = System.currentTimeMillis();
    }
}
